import socket
import selectors
import threading
import logging


FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
logging.basicConfig(format=FORMAT, level=logging.INFO)


class ChatServer:
    def __init__(self, ip='127.0.0.1', port=60000):
        self.laddr = (ip, port)
        self.sock = socket.socket()
        self.sock.bind(self.laddr)
        self.sock.listen()
        self.sock.setblocking(False)
        self.event = threading.Event()
        self.selector = selectors.DefaultSelector()
        self.clients = {}

    def __accept(self, client:socket.socket, mask):
        newsock, raddr = client.accept()
        self.clients[raddr] = newsock

        logging.info('IN ACCEPT new client socket {} in accept'.format(newsock))
        newkey = self.selector.register(newsock, selectors.EVENT_READ, self.__recv)
        logging.info('IN ACCEPT from client accpet: {},  key: {}'.format(client, newkey))

    def __recv(self, client:socket.socket, mask):
        try:
            data = client.recv(1024).strip().decode()
            logging.info('IN RECV data: {}'.format(data))
            logging.info('IN RECV clients: {}'.format(self.clients))
            if data == 'quit' or data == '':
                self.clients.pop(client.getpeername())
                self.selector.unregister(client)

            msg = 'from {}. msg: {}'.format(
                client.getpeername(),
                data
            ).encode()

            for client in self.clients.values():
                client.send(msg)
        except Exception:
            self.selector.unregister(client)
            pass

    def start(self):
        key = self.selector.register(self.sock, selectors.EVENT_READ, self.__accept)
        logging.info('IN START.Conn key {}'.format(key))

        while not self.event.is_set():
            events = self.selector.select()
            logging.info('IN START Events: {}'.format(
                events,
            ))
            for key, mask in events:
                callback = key.data
                logging.info('IN START key: {}, mask: {}'.format(key, mask))
                callback(key.fileobj, mask)

    def stop(self):
        self.event.set()
        self.event.wait(2)
        self.sock.close()
        pass


def main():
    cs = ChatServer(ip='10.70.1.192')
    threading.Thread(target=cs.start, name='chatstart', daemon=True).start()
    while True:
        cmd = input('>>> ').strip()
        if cmd == 'quit':
            cs.stop()
            break
        print(threading.enumerate())


if __name__ == '__main__':
    main()
